﻿using System;
using System.IO;
using System.Xml;
using System.Xml.Serialization;
using UnityEngine;

public class Game {
	// === Public =====================================================================================================
	public static string LevelFullName { get; private set; }

	public static GameModel GetGameModel() {
		return _model;
	}

	public static bool HasOpenedLevel() {
		return _model != null;
	}

	// --- Create | Save | Load | Close ---
	public static void CreateNewLevel() {
		CheckNoOpenedLevel();
		_model = new GameModel();

		LevelFullName = "Editor_" + DateTime.Now.Ticks;
	}

	public static bool EditorSaveLevel() {
		CheckOpenedLevel();
		if (!string.IsNullOrEmpty(LevelFullName)) {
			EditorSaveLevelAs(LevelFullName);
			return true;
		}
		return false;
	}

	public static void EditorSaveLevelAs(string fullName) {
		CheckOpenedLevel();
		if (string.IsNullOrEmpty(fullName)) {
			throw new ArgumentNullException(MyType + " fullName is null");
		}
		if (!Tools.FileName.GetExtension(fullName).Equals(FileConstants.Extensions.XML)) {
			fullName += FileConstants.Separators.EXTENSION + FileConstants.Extensions.XML;
		}
		EditorSaveLevelAsInner(fullName);
		LevelFullName = fullName;
	}

	public static void LoadLevelFromFile(string fullName) {
		if (string.IsNullOrEmpty(fullName)) {
			throw new ArgumentNullException(fullName + "' is null/empty");
		}
		if (!File.Exists(fullName)) {
			throw new ArgumentNullException(fullName + "' doesn't exist");
		}
		CloseLevel();
		LoadLevelFromFileInner(fullName);
	}

	public static void CloseLevel() {
		CheckOpenedLevel();
		_model = null;
	}

	// === Private ====================================================================================================
	private static GameModel _model;
	private static readonly Type MyType;

	static Game() {
		MyType = typeof(Game);
	}

	private static void CheckNoOpenedLevel() {
		if (HasOpenedLevel()) {
			throw new ArgumentException(MyType + " has already opened level.");
		}
	}

	private static void CheckOpenedLevel() {
		if (!HasOpenedLevel()) {
			throw new ArgumentException(MyType + " has no opened level.");
		}
	}

	private static void EditorSaveLevelAsInner(string fullName) {
		Tools.Map.OffsetPositionToZero(_model.Elements);
		if (!Tools.Map.CheckBoundaryPerimeter(_model.Elements)) {
			Debug.LogWarning("Not completed border");
			return;
		}
		var modelXml = new GameModelXml(_model);
		var xmlDoc = new XmlDocument();
		var xmlSerializer = new XmlSerializer(modelXml.GetType());
		using (var xmlWriter = xmlDoc.CreateNavigator().AppendChild()) {
			xmlSerializer.Serialize(xmlWriter, modelXml);
		}
		xmlDoc.Save(fullName);
	}

	private static void LoadLevelFromFileInner(string fullName) {
		var xmlDoc = new XmlDocument();
		xmlDoc.Load(fullName);
		LevelFullName = fullName;
		LoadLevelFromXml(xmlDoc);
	}

	private static void LoadLevelFromXml(XmlDocument xmlDoc) {
		var modelXml = XmlTools.Deserialize<GameModelXml>(xmlDoc);
		_model = new GameModel();
		_model.AddElement(modelXml.ElementsToXml);
	}
}
